/*
 * claplanparser.cpp
 *
 *  Created on: 30 Oct 2013
 *      Author: Tom Vercauteren
 */

#include "claplan_parser.h"

#include <vector>
#include "dvhp.h"
#include "string_utilities.h"

using namespace std;

bool Parser::isComment(const std::string &line)
{
	string tl = trim(line, " ");
	tl = trim(line, "\t");
	if (tl.size() > 0)
	{
		if (tl.at(0) == '#') return true;
	}
	return false;
}

bool Parser::isSection(const std::string &line)
{
	if (line == "header :" || line == "optimization :"
			|| line == "fractionation :" || line == "target :"
			|| line == "oar :" || line == "beam :") return true;
	if (line == "header" || line == "optimization" || line == "fractionation"
			|| line == "target" || line == "oar" || line == "beam") return true;
	return false;
}

int HeaderParser::parse(const std::string &line)
{
	if (header == 0)
	{
		cerr << "HeaderParser::parse : header pointer is null" << endl;
		return -1;
	}

	string sl = trim(line, " ");
	sl = trim(sl, "\t");

	vector<string> tokens;
	unsigned long i, c = 0, n = split(sl, tokens, "=", true);

	if (n < 2) return 0;
	if (n > 2) return -1;
//	cout << "split n : " << n << endl;
	for (i = 0; i < n; i++)
	{
		string tmp = tokens.at(i);
		tokens.at(i) = trim(tmp, " ");
	}

	try
	{
		string key = tokens.at(0);
		if (key == "version")
		{
			header->setVersion(str2int(tokens.at(1)));
			c++;
		}
		else if (key == "mode")
		{
			header->setMode(str2int(tokens.at(1)));
			c++;
		}
		else throw "unknown keyword";
	} catch (const char * e)
	{
		if (i < n) cerr << "HeaderParser::parse exception : " << e
				<< " \tkeyword : " << tokens.at(i) << endl;
		else cerr << "HeaderParser::parse exception : " << e << endl;
		return -1;
	}

	return c;
}

int HeaderParser::parse(std::istream &input)
{
	if (header == 0)
	{
		cerr << "HeaderParser::parse : header pointer is null" << endl;
		return -1;
	}
	string l;
	std::getline(input, l);
//	cout << __LINE__ << " : " << l << endl;
	int rv = 0, trv = 0;
	int mp = 2; // maximum number of parameters in the claplan header
	l = trim(l, " ");
	l = trim(l, "\t");
	while (l != "" && l != "\n" && l != "\r\n" && l.size() > 0 && rv < mp)
	{
		if (isComment(l)) continue;
		trv = parse(l);
		if (trv < 0) return -1;
		else if (trv == 1) rv++;
		else if (trv == 0 && rv == mp) break;
		std::getline(input, l);
	}
	return rv;
}

int OptimizationParser::parse(const std::string &line)
{
	if (op == 0)
	{
		cerr << "OptimizationParser::parse : OptParam pointer is null" << endl;
		return -1;
	}

	string sl = trim(line, " ");
	sl = trim(sl, "\t");

	vector<string> tokens;
	unsigned long i, c = 0, n = split(sl, tokens, "=", true);

	if (n < 2) return 0;
	if (n > 2) return -1;
//	cout << "split n : " << n << endl;
	for (i = 0; i < n; i++)
	{
		string tmp = tokens.at(i);
		tokens.at(i) = trim(tmp, " ");
	}

	try
	{
		string key = tokens.at(0);
		if (key == "routine")
		{
			op->setRoutine(tokens.at(1));
			c++;
		}
		else if (key == "max_time")
		{
			op->setMaxTime(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "tol")
		{
			op->setTol(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "rtol")
		{
			op->setRtol(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "mirw")
		{
			op->setMirw(str2long(tokens.at(1)));
			c++;
		}
		else if (key == "nskew")
		{
			op->setNskew(str2long(tokens.at(1)));
			c++;
		}
		else if (key == "disp_quant")
		{
			op->setDispQuant(str2long(tokens.at(1)));
			c++;
		}
		else throw "unknown keyword";
	} catch (const char * e)
	{
		if (i < n) cerr << "HeaderParser::parse exception : " << e
				<< " \tkeyword : " << tokens.at(i) << endl;
		else cerr << "HeaderParser::parse exception : " << e << endl;
		return -1;
	}

	return c;
}

int OptimizationParser::parse(std::istream &input)
{
	if (op == 0)
	{
		cerr << "OptimizationParser::parse : OptParam pointer is null" << endl;
		return -1;
	}
	string l;
	std::getline(input, l);
//	cout << __LINE__ << " : " << l << endl;
	int rv = 0, trv = 0;
	int mp = 7; // maximum number of parameters in the claplan optimization section
	l = trim(l, " ");
	l = trim(l, "\t");
	while (l != "" && l != "\n" && l != "\r\n" && l.size() > 0 && rv < mp)
	{
		if (isComment(l)) continue;
		trv = parse(l);
		if (trv < 0) return -1;
		else if (trv == 1) rv++;
		else if (trv == 0 && rv == mp) break;
		std::getline(input, l);
	}
	return rv;
}

int FractionationParser::parse(const std::string &line)
{
	if (data == 0)
	{
		cerr << "FractionationParser::parse : data pointer is null" << endl;
		return -1;
	}

	string sl = trim(line, " ");
	sl = trim(sl, "\t");

	vector<string> tokens;
	unsigned long i, c = 0, n = split(sl, tokens, "=", true);

	if (n < 2) return 0;
	if (n > 2) return -1;
//	cout << "split n : " << n << endl;
	for (i = 0; i < n; i++)
	{
		string tmp = tokens.at(i);
		tokens.at(i) = trim(tmp, " ");
	}

	try
	{
		string key = tokens.at(0);
		if (key == "nfrac")
		{
			data->nfrac = str2double(tokens.at(1));
			c++;
		}
		else throw "unknown keyword";
	} catch (const char * e)
	{
		cerr << "FractionationParser::parse exception : " << e << " \t : \""
				<< tokens.at(0) << "\"" << endl;
		return -1;
	}
	return c;
}

int FractionationParser::parse(std::istream &input)
{
	string l;
	int rv = 0, trv = 0;
	if (data == 0)
	{
		cerr << "FractionationParser::parse : data pointer is null" << endl;
		return -1;
	}
	while (!input.eof())
	{
		std::getline(input, l);
//		cout << __LINE__ << " : " << l << endl;
		if (!l.empty() && l != "\n" && l != "\r\n")
		{
			if (isComment(l)) continue;
			trv = parse(l);
			if (trv < 0) return -1;
			else if (trv > 0) return 1;
		}
	}
	return rv;
}

int TargetParser::parse(const std::string &line)
{
	if (data == 0)
	{
		cerr << "TargetParser::parse : data pointer is null" << endl;
		return -1;
	}
	if (ct == 0)
	{
		cerr << "TargetParser::parse : ct pointer is null" << endl;
		return -1;
	}

	string sl = trim(line, " ");
	sl = trim(sl, "\t");

	vector<string> tokens;
	unsigned long i, c = 0, n = split(sl, tokens, "=", true);

	if (n < 2) return 0;
	if (n > 2) return -1;
//	cout << "split n : " << n << endl;
	for (i = 0; i < n; i++)
	{
		string tmp = tokens.at(i);
		tokens.at(i) = trim(tmp, " ");
	}

	try
	{
		string key = tokens.at(0);
		if (key == "anastruct")
		{
			ct->setName(tokens.at(1));
			c++;
		}
		else if (key == "TCP_model")
		{
			ct->setTcpModel(tokens.at(1));
			c++;
		}
		else if (key == "TCP_name")
		{
			ct->setTcpName(tokens.at(1));
			c++;
		}
		else if (key == "impf")
		{
			ct->setImpf(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "pf_inhom")
		{
			ct->setPfInhom(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "pf_rms")
		{
			ct->setPfRms(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "pf_rmsdiffs")
		{
			vector<string> ttokens;
			unsigned long tn = split(tokens.at(1), ttokens, ";", true);
			if (tn != 2)
				throw "expected 2 values for the pf_rmsdiffs parameter";
			double d[tn];
			for (i = 0; i < tn; i++)
				d[i] = str2double(trim(ttokens.at(i), " "));
			ct->setPfRmsdiffs(d);
			c += tn;
		}
		else if (key == "pf_ihlf")
		{
			vector<string> ttokens;
			unsigned long tn = split(tokens.at(1), ttokens, ";", true);
			if (tn != 3) throw "expected 3 values for the pf_ihlf parameter";
			double d[tn];
			for (i = 0; i < tn; i++)
				d[i] = str2double(trim(ttokens.at(i), " "));
			ct->setPfIhlf(d);
			c += tn;
		}
		else if (key == "pfs_olf")
		{
			vector<string> ttokens;
			unsigned long tn = split(tokens.at(1), ttokens, ";", true);
			if (tn != 3) throw "expected 3 values for the pf_olf parameter";
			double d[tn];
			for (i = 0; i < tn; i++)
				d[i] = str2double(trim(ttokens.at(i), " "));
			ct->setPfsOlf(d);
			c += tn;
		}
		else if (key == "dvhp")
		{
			vector<string> ttokens;
			unsigned long tn = split(tokens.at(1), ttokens, ";", true);
			if (tn != 4) throw "expected 4 values for the dvhp parameter";
			Dvhp dvhp;
			dvhp.w = str2double(trim(ttokens.at(0), " "));
			dvhp.d = str2double(trim(ttokens.at(1), " "));
			dvhp.v = str2double(trim(ttokens.at(2), " "));
			dvhp.r = str2double(trim(ttokens.at(3), " "));
			ct->dvhps.push_back(dvhp);
			c += tn;
		}
		else if (key == "tracer")
		{
			ct->setTracer(tokens.at(1));
			c++;
		}
		else if (key == "impf_pet")
		{
			ct->setImpfPet(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "rimpf_petd")
		{
			ct->setRimpfPetd(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "pet_low")
		{
			ct->setPetLow(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "dose_low")
		{
			ct->setDoseLow(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "pet_high")
		{
			ct->setPetHigh(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "dose_high")
		{
			ct->setDoseHigh(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "c_pf_inhom")
		{
			ct->setCPfInhom(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "c_pf_rms")
		{
			ct->setCPfRms(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "cmi")
		{
			vector<string> ttokens;
			unsigned long tn = split(tokens.at(1), ttokens, ";", true);
			if (tn != 2) throw "expected 2 values for the cmi parameter";
			double d[tn];
			for (i = 0; i < tn; i++)
				d[i] = str2double(trim(ttokens.at(i), " "));
			ct->setCmi(d);
			c += tn;
		}
		else throw "unknown keyword";
	} catch (const char * e)
	{
		cerr << "TargetParser::parse exception : " << e << " \t : \""
				<< tokens.at(0) << "\"" << endl;
		return -1;
	}
	return c;
}

int TargetParser::parse(std::istream &input)
{
	string l;
	int rv = 0, trv = 0;
	if (data == 0)
	{
		cerr << "TargetParser::parse : data pointer is null" << endl;
		return -1;
	}
	streampos pos;
	while (!input.eof())
	{
		pos = input.tellg();
		std::getline(input, l);
		if (isSection(l))
		{
			// found a section go back one line and give the control back to the caller
			input.seekg(pos);
			if (ct != 0)
			{
				data->targets.push_back(*ct);
				delete ct;
				ct = 0;
			}
			return rv;
		}
//		cout << __LINE__ << " : " << l << endl;
		if (!l.empty() && l != "\n" && l != "\r\n")
		{
			if (isComment(l)) continue;
			if (ct == 0) ct = new ClaplanTarget();
			trv = parse(l);
			if (trv < 0)
			{
				delete ct;
				ct = 0;
				return -1;
			}
			else if (trv > 0) rv += trv;
		}
	}
	if (ct != 0)
	{
		data->targets.push_back(*ct);
		delete ct;
		ct = 0;
	}
	return rv;
}

int OarParser::parse(const std::string &line)
{
	if (data == 0)
	{
		cerr << "OarParser::parse : data pointer is null" << endl;
		return -1;
	}
	if (co == 0)
	{
		cerr << "OarParser::parse : co pointer is null" << endl;
		return -1;
	}

	string sl = trim(line, " ");
	sl = trim(sl, "\t");

	vector<string> tokens;
	unsigned long i, c = 0, n = split(sl, tokens, "=", true);

	if (n < 2) return 0;
	if (n > 2) return -1;
//	cout << "split n : " << n << endl;
	for (i = 0; i < n; i++)
	{
		string tmp = tokens.at(i);
		tokens.at(i) = trim(tmp, " ");
	}

	try
	{
		string key = tokens.at(0);
		if (key == "anastruct")
		{
			co->setAnastruct(tokens.at(1));
			c++;
		}
		else if (key == "NTCP_model")
		{
			co->setNtcpModel(tokens.at(1));
			c++;
		}
		else if (key == "NTCP_name")
		{
			co->setNtcpName(tokens.at(1));
			c++;
		}
		else if (key == "impf")
		{
			co->setImpf(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "cmi")
		{
			vector<string> ttokens;
			unsigned long tn = split(tokens.at(1), ttokens, ";", true);
			if (tn != 2) throw "expected 2 values for the cmi parameter";
			double d[tn];
			for (i = 0; i < tn; i++)
				d[i] = str2double(trim(ttokens.at(i), " "));
			co->setCmi(d);
			c += tn;
		}
		else throw "unknown keyword";
	} catch (const char * e)
	{
		cerr << "OarParser::parse exception : " << e << " \t : \""
				<< tokens.at(0) << "\"" << endl;
		return -1;
	}
	return c;
}

int OarParser::parse(std::istream &input)
{
	string l;
	int rv = 0, trv = 0;
	if (data == 0)
	{
		cerr << "OarParser::parse : data pointer is null" << endl;
		return -1;
	}
//	cout << __FILE__ << " : " << __LINE__ << endl;
	streampos pos;
	while (!input.eof())
	{
		pos = input.tellg();
		std::getline(input, l);
		if (isSection(l))
		{
			// found a section go back one line and give the control back to the caller
			input.seekg(pos);
			if (co != 0)
			{
				data->oars.push_back(*co);
				delete co;
				co = 0;
			}
			return rv;
		}
//		cout << __LINE__ << " : " << l << endl;
		if (!l.empty() && l != "\n" && l != "\r\n")
		{
			if (isComment(l)) continue;
			if (co == 0) co = new ClaplanOar();
			trv = parse(l);
			if (trv < 0)
			{
				delete co;
				co = 0;
				return -1;
			}
			else if (trv > 0) rv += trv;
		}
	}
	if (co != 0)
	{
		data->oars.push_back(*co);
		delete co;
		co = 0;
	}
	return rv;
}

int BeamParser::parse(const std::string &line)
{
	if (data == 0)
	{
		cerr << "BeamParser::parse : data pointer is null" << endl;
		return -1;
	}
	if (cb == 0)
	{
		cerr << "BeamParser::parse : cb pointer is null" << endl;
		return -1;
	}

	string sl = trim(line, " ");
	sl = trim(sl, "\t");

	vector<string> tokens;
	unsigned long i, c = 0, n = split(sl, tokens, "=", true);

	if (n < 2) return 0;
	if (n > 2) return -1;
//	cout << "split n : " << n << endl;
	for (i = 0; i < n; i++)
	{
		string tmp = tokens.at(i);
		tokens.at(i) = trim(tmp, " ");
	}

	try
	{
		string key = tokens.at(0);
		if (key == "type_bg")
		{
			cb->setTypeBg(str2int(tokens.at(1)));
			c++;
		}
		else if (key == "isocenter_anastruct")
		{
			cb->setIsocenterAnastruct(tokens.at(1));
			c++;
		}
		else if (key == "gantry")
		{
			cb->setGantry(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "table")
		{
			cb->setTable(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "coll")
		{
			cb->setColl(str2double(tokens.at(1)));
			c++;
		}
		else if (key == "linac")
		{
			cb->setLinac(tokens.at(1));
			c++;
		}
		else if (key == "modality")
		{
			if (tokens.at(1) == "photons") cb->setModality(
					ENERGY_MODALITIES_PHOTONS);
			else if (tokens.at(1) == "electrons") cb->setModality(
					ENERGY_MODALITIES_ELECTRONS);
			else
			{
				cb->setModality(ENERGY_MODALITIES_UNDEFINED);
				throw "energy modality (MV / MeV) is undefined";
			}
			c++;
		}
		else if (key == "energy")
		{
			vector<string> ttokens;
			unsigned long tn = split(tokens.at(1), ttokens, " ", true);
			if (tn != 2) throw "expected an energy and unit parameter";
			if (ttokens.at(1) == "MV"
					&& cb->getModality() != ENERGY_MODALITIES_PHOTONS)
				throw "Energy modality does not fit the energy unit (MV)";
			if (ttokens.at(1) == "MeV"
					&& cb->getModality() != ENERGY_MODALITIES_ELECTRONS)
				throw "Energy modality does not fit the energy unit (MeV)";
			cb->setEnergy(str2double(ttokens.at(0)));
			c++;
		}
		else if (key == "accessory")
		{
			cb->setAccessory(tokens.at(1));
			c++;
		}
		else if (key == "wedge")
		{
			cb->setWedge(tokens.at(1));
			c++;
		}
		else if (key == "contoured_anastruct")
		{
			cb->setContouredAnastruct(tokens.at(1));
			c++;
		}
		else if (key == "margin")
		{
			cb->setMargin(str2double(tokens.at(1)));
			c++;
		}
		else throw "unknown keyword";
	} catch (const char * e)
	{
		cerr << "BeamParser::parse exception : " << e << " \t : \""
				<< tokens.at(0) << "\"" << endl;
		return -1;
	}
	return c;
}

int BeamParser::parse(std::istream &input)
{
	string l;
	int rv = 0, trv = 0;
	if (data == 0)
	{
		cerr << "BeamParser::parse : data pointer is null" << endl;
		return -1;
	}
//	cout << __FILE__ << " : " << __LINE__ << endl;
	streampos pos;
	while (!input.eof())
	{
		pos = input.tellg();
		std::getline(input, l);
		if (isSection(l))
		{
			// found a section go back one line and give the control back to the caller
			input.seekg(pos);
			if (cb != 0)
			{
				data->beams.push_back(*cb);
				delete cb;
				cb = 0;
			}
			return rv;
		}
//		cout << __LINE__ << " : " << l << endl;
		if (!l.empty() && l != "\n" && l != "\r\n")
		{
			if (isComment(l)) continue;
			if (cb == 0) cb = new ClaplanBeam();
			trv = parse(l);
			if (trv < 0)
			{
				delete cb;
				cb = 0;
				return -1;
			}
			else if (trv > 0) rv += trv;
		}
	}
	if (cb != 0)
	{
		data->beams.push_back(*cb);
		delete cb;
		cb = 0;
	}
	return rv;
}

int ClaplanParser::parse(const std::string &line)
{
	string sl = trim(line, " ");
	sl = trim(sl, "\t");

	if (data == 0)
	{
		cerr << "ClaplanParser::parse : data pointer is null" << endl;
		return -1;
	}

	if (line == "header :") return HEADER;
	else if (line == "optimization :") return OPTIMIZATION;
	else if (line == "fractionation :") return FRACTIONATION;
	else if (line == "target :") return TARGET;
	else if (line == "oar :") return OAR;
	else if (line == "beam :") return BEAM;
	else if (line.empty()) return EMTPY;
	else return KEY_VALUE;
	return -1;
}

int ClaplanParser::parse(std::istream &input)
{
	string l;
	int trv = 0;
	if (data == 0)
	{
		cerr << "ClaplanParser::parse : data pointer no allocated "
				"-> parsing stopped." << endl;
		return -1;
	}
	while (!input.eof())
	{
		std::getline(input, l);
//		cout << __LINE__ << " : " << l << endl;
		if (isComment(l)) continue;
		if (!l.empty() && l != "\n" && l != "\r\n")
		{
			trv = parse(l);
			switch (trv)
			{
			case HEADER:
//				cout << "claplan header section" << endl;
				headerParser->header = &data->header;
				parser = headerParser;
				break;
			case OPTIMIZATION:
//				cout << "claplan optimization section" << endl;
				optParser->op = &data->opt;
				parser = optParser;
				break;
			case FRACTIONATION:
//				cout << "claplan fractionation section" << endl;
				fracParser->data = data;
				parser = fracParser;
				break;
			case TARGET:
//				cout << "claplan target section" << endl;
				targetParser->data = data;
				parser = targetParser;
				break;
			case OAR:
//				cout << "claplan oar section" << endl;
				oarParser->data = data;
				parser = oarParser;
				break;
			case BEAM:
//				cout << "claplan beam section" << endl;
				beamParser->data = data;
				parser = beamParser;
				break;
			case EMTPY:
				break;
			default:
				break;
			}
			if (parser != 0) if (parser->parse(input) < 0) return -1;
			parser = 0;
		}
	}
	return 0;
}

